/**********************************************************************
 *   Copyright: (C)2024 LingYun IoT System Studio
 *      Author: LiJiahui<2199250859@qq.com>
 *
 * Description: Temperature and humidity sensor SHT20 driver on ISKBoard
 *
 *   ChangeLog:
 *        Version    Date       Author            Description
 *        V1.0.0  2024.08.29    LiJiahui      Release initial version
 *
 ***********************************************************************/

#include <stdio.h>
#include <string.h>

#include "i2c_bitbang.h"
#include "sht20.h"

//#define	CONFIG_DEBUG_SHT2X /* Enable SHT20 debug */

#define SHT2X_CHIPADDR	0x40 /* SHT20 7-Bits Chip address */

/* 如果启用了调试模式，sht2x_print会映射到printf，用于输出调试信息
 * 否则，sht2x_print会被替换为空操作，避免生成冗余代码
 */
#ifdef	CONFIG_DEBUG_SHT2X
#define sht2x_print(format, args...) printf(format, ##args)
#else
#define sht2x_print(format, args...) do{} while(0);
#endif

/* 仅在调试模式下有效，用于以十六进制打印缓冲区内容 */
#ifdef CONFIG_DEBUG_SHT2X
static inline void dump_buf(uint8_t *buf, uint8_t size)
{
	int	i;

	if( !buf )
		return ;

	for(i=0; i<size; i++)
		printf("%02x ", buf[i]);

	printf("\r\n");
}
#endif


enum
{
	TRIG_T_MEASUREMENT_HM 		= 0xE3, /* 触发温度测量（Holder Master模式） */
	TRIG_RH_MEASUREMENT_HM 		= 0xE5, /* 触发湿度测量（Holder Master模式） */
	TRIG_T_MEASUREMENT_POLLL 	= 0xF3, /* 触发温度测量（no hold master主模式） */
	TRIG_RH_MEASUREMENT_POLLL 	= 0xF5, /* 触发湿度测量（no hold master主模式） */
	USER_REG_W 					= 0xE6, /* 写用户寄存器 */
	USER_REG_R 					= 0xE7, /* 读用户寄存器 */
	SOFT_RESET 					= 0xFE  /* 软复位 */
};

static int sht2x_measure_value(uint8_t command, uint16_t *val);
static int sht2x_softreset(void);

/* 读取温湿度数据并转换为实际值 */
int sht20_sample_Trh(float *temperature, float *humdity)
{
	uint16_t	raw_temp, raw_rh;
	float		temp, rh;
	int			rv;

	i2c_lock(SHT2X_CHIPADDR);

	if( sht2x_softreset() < 0 )
	{
		sht2x_print("SHT20 soft reset failed.\r\n");
		goto OUT;
	}

	sht2x_print("Start to measurement temperature...\r\n");
	rv = sht2x_measure_value(TRIG_T_MEASUREMENT_POLLL, &raw_temp);
	if( rv < 0 )
	{
		sht2x_print("I2C measurement temperature failed, rv=%d\r\n", rv);
		goto OUT;
	}

	/* 此处不直接将temperature赋值公式原因：检查参数有效性，避免传入的是NULL（使用者可能不关心温度）
	 * 更安全、更灵活、更高效
	 */
	temp = -46.85f + 175.72f * (float)raw_temp / 65536.0f;
	sht2x_print("Measure temperature:%.2f\r\n", temp);
	if( temperature )
		*temperature = temp;

	sht2x_print("Start to measurement relative humidity...\r\n");
	rv = sht2x_measure_value(TRIG_RH_MEASUREMENT_POLLL, &raw_rh);
	if( rv < 0 )
	{
		sht2x_print("I2C measurement humidity failed, rv=%d\r\n", rv);
		goto OUT;
	}

	rh = -6.0f + 125.0f * (float)raw_rh / 65536.0f;
	sht2x_print("Measure relative humidity :%.1f%%\r\n", rh);
	if( humdity )
		*humdity = rh;

OUT:
	i2c_free();
	return rv;
}

int sht2x_softreset(void)
{
	uint8_t	command = SOFT_RESET;
	int		rv = 0;

	sht2x_print("Start soft reset sht2x\r\n");


	rv = i2c_write(&command, 1);
	if( rv != HAL_OK )
	{
		sht2x_print("SHT2X send soft reset command 0x%0x failure :rv = 0x%02x\r\n", command, rv);
		return -rv;
	}

	/* 软复位后，SHT20需要约15ms初始化 */
	HAL_Delay(15);
	return 0;
}

static int sht2x_checkcrc(uint8_t *data, uint8_t bytes, uint8_t checksum)
{
	uint8_t crc = 0;
	uint8_t	i;
	uint8_t	bit;

	/* 使用多项式0x0131计算CRC */
	for(i=0; i<bytes; i++)
	{
		crc ^= data[i];
		for(bit=8; bit>0; --bit)
		{
			if(crc & 0x80)
				crc = (crc << 1) ^ 0x0131;
			else
				crc = (crc << 1);
		}
	}

	if( crc != checksum )
		return -1;
	else
		return 0;
}


/* 发送测量命令，读取原始数据并校验 */
static int sht2x_measure_value(uint8_t command, uint16_t *val)
{
	uint8_t	buf[3]; /* 接收缓冲区（2字节数据+1字节CRC）*/
	int		count = 4; /* 重试次数 */

	if( !val )
	{
		sht2x_print("SHT2X invalid input arguments\r\n");
		return -1;
	}

	if( TRIG_T_MEASUREMENT_POLLL != command && TRIG_RH_MEASUREMENT_POLLL != command )
	{
		sht2x_print("SHT2X unsupport command :0x%0x\r\n", command);
		return -2;
	}

	if( i2c_write(&command, 1) < 0 )
	{
		sht2x_print("HST2X send measure command 0x%0x failure\r\n", command);
		return -3;
	}

	/* 等待测量完成（温度：85ms， 湿度：29ms） */
	if( TRIG_T_MEASUREMENT_POLLL == command )
		HAL_Delay(85);
	else
		HAL_Delay(29);

	while(count--)
	{
		memset(buf, 0, 3);

		if( !i2c_read(buf, 3) )
		{
			break;
		}
		HAL_Delay(5);
	}

	if( sht2x_checkcrc(buf, 2, buf[2]) < 0 )
	{
#ifdef	CONFIG_DEBUG_SHT2X
		sht2x_print("Measurement data checksum failure:\r\n");
		dump_buf(buf, 3);
#endif
		return -4;
	}

	if( TRIG_T_MEASUREMENT_POLLL == command )
		*val = buf[0]<<8|(buf[1]&0xFC); /* 14bits(1111 1100) */
	else
		*val = buf[0]<<8|(buf[1]&0xFF); /* 12bits(1111 1111) */

	sht2x_print("Measurement temperature value :0x%04x\r\n", *val);

	return 0;
}
